Skip to content

Conversation

@adsharma
Copy link

@adsharma adsharma commented Jan 17, 2025

The @sqlmodel decorator introduced by fquery.sqlmodel moves schema definitions and types closer to the object model (references) instead of foreign_keys.

Instead of writing:

class Hero(SQLModel, table=True):
    ...
    team_id: int | None = Field(default=None, foreign_key="team.id")

you could write:

@sqlmodel
class Hero:
    ...
    team: Team | None = foreign_key("team.id")

Everything works the same as before. Only the syntax has changed. However, if Team also has a foreign key to Hero, then one of them will have to use forward declaration so type checkers work.

In those cases we need a two pass solution where in the second pass, with the full type definition available, we can generate the correct code.

Refactor some of the existing code to introduce sqlmodel_rebuild().

Use cases and benefits

  • Model relationships (SQLModel objects) instead of exposing ids - an implementation detail. If someone wants to use UUID instead of int, it could be handled via meta programming instead of having to rewrite all the models.

  • Declarative/Flexible ID generation. Do you want each table to have a different ID space or keep them all in one global ID space where objects are guaranteed to have unique IDs regardless of the type.

  • Multi-model use cases - relational, columnar and graph. Each one could use its own decorator that stacks on top of @sqlmodel. fquery uses the labelled property graph approach (LPG). Such graphs can be further exposed via graphql or related technologies.

  • Startup performance in a large repo containing 1000 sqlmodels. By deferring some of the initialization we get faster load times. The rest of the initialization could happen only when the model is instantiated.

The decorator is in a self contained python file and can be moved to a standalone library independent of fquery or be included as a part of this PR.

@adsharma adsharma marked this pull request as draft January 17, 2025 01:27
@adsharma adsharma marked this pull request as ready for review January 17, 2025 04:56
@svlandeg svlandeg marked this pull request as draft February 24, 2025 10:39
@svlandeg svlandeg changed the title Support sqlmodel_rebuild, similar to pydantic ✨ Support sqlmodel_rebuild Feb 24, 2025
@svlandeg svlandeg added the feature New feature or request label Feb 24, 2025
@adsharma adsharma marked this pull request as ready for review February 24, 2025 18:44
Linter doesn't understand the special call handling for this case.
@adsharma

This comment was marked as resolved.

@svlandeg

This comment was marked as resolved.

@svlandeg svlandeg marked this pull request as draft February 27, 2025 08:35
@adsharma

This comment was marked as resolved.

@adsharma

This comment was marked as resolved.

@svlandeg

This comment was marked as resolved.

@adsharma

This comment was marked as resolved.

@adsharma
Copy link
Author

adsharma commented Mar 1, 2025

@svlandeg tests are all green. Please review.

@svlandeg svlandeg marked this pull request as ready for review March 3, 2025 07:10
@YuriiMotov
Copy link
Member

@adsharma, are there any other use cases beside the use case with external library you've mentioned?
Could it be useful for something else?

@adsharma
Copy link
Author

adsharma commented Oct 8, 2025

@YuriiMotov here's how I would like this work to be viewed:

The most general case is using meta programming to hide implementation details behind Pydantic and SQLModel.

In a large repository of many thousand SQLModels, you might want to defer work. Otherwise, model loading becomes slow and expensive.

Anytime you want to do multiple passes over the objects, you'll need the rebuild capability. So no, it's not something specific to my pet python library. Pydantic already implements it. But it's not lazy enough.

@YuriiMotov
Copy link
Member

Thanks for quick reply!

In a large repository of many thousand SQLModels, you might want to defer work. Otherwise, model loading becomes slow and expensive.

But in current implementation (this PR) initialization is not deferred, right? It just allows to re-initialize model later.

Anytime you want to do multiple passes over the objects, you'll need the rebuild capability. So no, it's not something specific to my pet python library

I'm afraid it's a bit too abstract. Wen need more clear reasoning to move it forward.

I can imagine someone may want to dynamically add fields using decorators (e.g. for Multi-tenancy).
Like:

@add_client_specific_fields(client_id)
class Hero(SQLModel, table-True):
    pass

Is it a valid use case you have in mind for this feature?
Can you add a few more use cases?

@adsharma
Copy link
Author

adsharma commented Oct 8, 2025

But in current implementation (this PR) initialization is not deferred, right? It just allows to re-initialize model later.

That's right. But this file works as a standalone library. If you prefer it to be a part of this PR, I'm happy to contribute.

Re-initialize the model later has many good use cases:

  • Model relationships (SQLModel objects) instead of exposing ids - an implementation detail. If someone wants to use UUID instead of int, it could be handled via meta programming instead of having to rewrite all the models.

  • Declarative/Flexible ID generation. Do you want each table to have a different ID space or keep them all in one global ID space where objects are guaranteed to have unique IDs regardless of the type.

Most importantly, on repos like schema-org-python such re-initialize later flow improves startup time. The repo has about 1000 models.

@adsharma
Copy link
Author

adsharma commented Oct 8, 2025

dynamically add fields using decorators

yes, this is a valid use case. So are stacking other decorators:

@node
@sqlmodel
class SomeModel:
 ...

Would allow this table to be used as a node in a labelled property graph. You can further expose them via graphql or similar.

@YuriiMotov
Copy link
Member

Would be nice if you could summarize use cases we discussed and update the description of this PR, so that we could hide some comments as resolved.
It would simplify the review process for Sebastian

@adsharma
Copy link
Author

adsharma commented Oct 8, 2025

Updated the summary. Please review.

@YuriiMotov
Copy link
Member

Updated the summary. Please review.

Unfortunately, I think you misunderstood me..
For now the description looks like a promotion of your library. But I think we should focus on the question "Why introducing sqlmodel_rebuild() method is beneficial for SQLModel and its users. Why Sebastian should prioritize this task other hundreds other tasks?

@adsharma
Copy link
Author

adsharma commented Oct 9, 2025

In the last sentence of the description, I address "is it a promotion of a particular library?". It's certainly a promotion of some of the methods I discuss. I'm agnostic about where the code lives.

Are there other compositional techniques (stacking decorators) possible? Certainly. It's really a question for fastapi user community to come up with use cases.

Here's a more complete end to end example utilizing this approach.

@github-actions github-actions bot removed the waiting label Oct 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants